home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Collection of Tools & Utilities
/
Collection of Tools and Utilities.iso
/
edit
/
elv18src.zip
/
ctags.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-01-17
|
21KB
|
908 lines
/* ctags.c */
/* This is a reimplementation of the ctags(1) program. It supports ANSI C,
* and has heaps o' flags. It is meant to be distributed with elvis.
*/
#include <stdio.h>
#ifdef __STDC__
# include <string.h>
# include <stdlib.h>
#endif
#include "config.h"
#ifndef FALSE
# define FALSE 0
# define TRUE 1
#endif
#ifndef TAGS
# define TAGS "tags"
#endif
#ifndef REFS
# define REFS "refs"
#endif
#ifndef BLKSIZE
# define BLKSIZE 1024
#endif
#include "ctype.c" /* yes, that really is the .c file, not the .h one. */
extern void file_open P_((char *));
extern int file_getc P_((void));
extern void file_ungetc P_((int));
extern void file_copyline P_((long, FILE *));
extern void cpp_open P_((char *));
extern void cpp_echo P_((int));
extern int cpp_getc P_((void));
extern void cpp_ungetc P_((int));
extern int lex_gettoken P_((void));
extern void maketag P_((int, long));
extern void ctags P_((char *));
extern void usage P_((void));
extern void main P_((int, char **));
/* -------------------------------------------------------------------------- */
/* Some global variables */
/* The following boolean variables are set according to command line flags */
int backward; /* -B regexp patterns search backwards */
int no_colons; /* -S make static tags look like global tags */
int incl_static; /* -s include static tags */
int incl_types; /* -t include typedefs and structs */
int incl_vars; /* -v include variables */
int make_refs; /* -r generate a "refs" file */
int append_files; /* -a append to "tags" [and "refs"] files */
#ifdef DEVNULL
int dont_make_tags; /* -T Don't make \"tags\" */
#endif
/* The following are used for outputting to the "tags" and "refs" files */
FILE *tags; /* used for writing to the "tags" file */
FILE *refs; /* used for writing to the "refs" file */
/* -------------------------------------------------------------------------- */
/* These are used for reading a source file. It keeps track of line numbers */
char *file_name; /* name of the current file */
FILE *file_fp; /* stream used for reading the file */
long file_lnum; /* line number in the current file */
long file_seek; /* fseek() offset to the start of current line */
int file_afternl; /* boolean: was previous character a newline? */
int file_prevch; /* a single character that was ungotten */
int file_header; /* boolean: is the current file a header file? */
/* This function opens a file, and resets the line counter. If it fails, it
* it will display an error message and leave the file_fp set to NULL.
*/
void file_open(name)
char *name; /* name of file to be opened */
{
/* if another file was already open, then close it */
if (file_fp)
{
fclose(file_fp);
}
/* try to open the file for reading. The file must be opened in
* "binary" mode because otherwise fseek() would misbehave under DOS.
*/
#if MSDOS || TOS || OS2
file_fp = fopen(name, "rb");
#else
file_fp = fopen(name, "r");
#endif
if (!file_fp)
{
perror(name);
}
/* reset the name & line number */
file_name = name;
file_lnum = 0L;
file_seek = 0L;
file_afternl = TRUE;
/* determine whether this is a header file */
file_header = FALSE;
name += strlen(name) - 2;
if (name >= file_name && name[0] == '.' && (name[1] == 'h' || name[1] == 'H'))
{
file_header = TRUE;
}
}
/* This function reads a single character from the stream. If the *previous*
* character was a newline, then it also increments file_lnum and sets
* file_offset.
*/
int file_getc()
{
int ch;
/* if there is an ungotten character, then return it. Don't do any
* other processing on it, though, because we already did that the
* first time it was read.
*/
if (file_prevch)
{
ch = file_prevch;
file_prevch = 0;
return ch;
}
/* if previous character was a newline, then we're starting a line */
if (file_afternl && file_fp)
{
file_afternl = FALSE;
file_seek = ftell(file_fp);
file_lnum++;
}
/* Get a character. If no file is open, then return EOF */
ch = (file_fp ? getc(file_fp) : EOF);
/* if it is a newline, then remember that fact */
if (ch == '\n')
{
file_afternl = TRUE;
}
/* return the character */
return ch;
}
/* This function ungets a character from the current source file */
void file_ungetc(ch)
int ch; /* character to be ungotten */
{
file_prevch = ch;
}
/* This function copies the current line out some other fp. It has no effect
* on the file_getc() function. During copying, any '\' characters are doubled
* and a leading '^' or trailing '$' is also quoted. The '\n' character is not
* copied. If the '\n' is preceded by a '\r', then the '\r' isn't copied.
*
* This is meant to be used when generating a tag line.
*/
void file_copyline(seek, fp)
long seek; /* where the lines starts in the source file */
FILE *fp; /* the output stream to copy it to */
{
long oldseek;/* where the file's pointer was before we messed it up */
char ch; /* a single character from the file */
char next; /* the next character from this file */
/* go to the start of the line */
oldseek = ftell(file_fp);
fseek(file_fp, seek, 0);
/* if first character is '^', then quote it */
ch = getc(file_fp);
#if 0
if (ch == '^')
{
putc('\\', fp);
}
#endif
/* write everything up to, but not including, the newline */
while (ch != '\n')
{
/* preread the next character from this file */
next = getc(file_fp);
/* if character is '\', or a terminal '$', then quote it */
if (ch == '\\'
|| ch == (backward ? '?' : '/')
|| (ch == '$' && next == '\n'))
{
putc('\\', fp);
}
/* copy the character, unless it is a terminal '\r' */
if (ch != '\r' || next != '\n')
putc(ch, fp);
/* next character... */
ch = next;
}
/* seek back to the old position */
fseek(file_fp, oldseek, 0);
}
/* -------------------------------------------------------------------------- */
/* This section handles preprocessor directives. It strips out all of the
* directives, and may emit a tag for #define directives.
*/
int cpp_afternl; /* boolean: look for '#' character? */
int cpp_prevch; /* an ungotten character, if any */
int cpp_refsok; /* boolean: can we echo characters out to "refs"? */
int cpp_refsnl; /* boolean: dup next \n in "refs"? */
/* This function opens the file & resets variables */
void cpp_open(name)
char *name; /* name of source file to be opened */
{
/* use the lower-level file_open function to open the file */
file_open(name);
/* reset variables */
cpp_afternl = TRUE;
cpp_refsok = TRUE;
}
/* This function copies a character from the source file to the "refs" file */
void cpp_echo(ch)
int ch; /* the character to copy */
{
static wasnl;
/* echo non-EOF chars, unless not making "ref", or echo turned off */
if (ch != EOF && make_refs && cpp_refsok && !file_header)
{
/* try to avoid blank lines */
if (ch == '\n')
{
/* hack: double \n at end of declarations, to
help `ref' find the right starting point...
*/
if (cpp_refsnl)
{
putc('\n', refs);
cpp_refsnl = FALSE;
}
if (wasnl)
{
return;
}
wasnl = TRUE;
}
else
{
wasnl = FALSE;
}
/* add the character */
putc(ch, refs);
}
}
/* This function returns the next character which isn't part of a directive */
int cpp_getc()
{
static
int ch; /* the next input character */
char *scan;
/* if we have an ungotten character, then return it */
if (cpp_prevch)
{
ch = cpp_prevch;
cpp_prevch = 0;
return ch;
}
/* Get a character from the file. Return it if not special '#' */
ch = file_getc();
if (ch == '\n')
{
cpp_afternl = TRUE;
cpp_echo(ch);
return ch;
}
else if (ch != '#' || !cpp_afternl)
{
/* normal character. Any non-whitespace should turn off afternl */
if (ch != ' ' && ch != '\t')
{
cpp_afternl = FALSE;
}
cpp_echo(ch);
return ch;
}
/* Yikes! We found a directive */
/* see whether this is a #define line */
scan = " define ";
while (*scan)
{
if (*scan == ' ')
{
/* space character matches any whitespace */
do
{
ch = file_getc();
} while (ch == ' ' || ch == '\t');
file_ungetc(ch);
}
else
{
/* other characters should match exactly */
ch = file_getc();
if (ch !=